package com.youlu.campus.web.data.service.impl;

import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.common.Constants;

import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.web.data.entity.DayFissionStatistics;
import com.youlu.campus.web.data.service.DayFissionStatisticsService;
import com.youlu.campus.web.common.utils.DateTimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class DayFissionStatisticsServiceImpl extends BaseService implements DayFissionStatisticsService {
    /**
     * 24小时过期
     */
    private final Long keyExpire = 24 * 60 * 60L;

    @Override
    @Async
    public void accumulationTaskParticipatingUsers(String tenantId, String taskId, String userId) {
        log.info(":>>> 开始组建任务-参与用户数,租户ID:{},任务ID:{},用户:{}", tenantId, taskId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组建任务累加-参与用户数参数错误");
            return;
        }
        String key = this.getTaskParticipatingUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 组建任务-参与用户数错误:{}", e);
        } finally {
            this.updateAccumulationTaskParticipatingUsers(tenantId, taskId, userId);
        }
    }

    @Override
    @Async
    public void accumulationTaskActiveUsers(String tenantId, String taskId, String userId) {
        log.info(":>>> 开始组建任务-激活用户数,租户ID:{},任务ID:{},用户:{}", tenantId, taskId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组建任务累加-激活用户数参数错误");
            return;
        }
        String key = this.getTaskActiveUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 组建任务-激活用户数错误:{}", e);
        } finally {
            this.updateAccumulationTaskActiveUsers(tenantId, taskId, userId);
        }
    }

    @Override
    @Async
    public void accumulationTaskAuthorizeUsers(String tenantId, String taskId, String userId) {
        log.info(":>>> 开始组建任务-授权用户数,租户ID:{},任务ID:{},用户:{}", tenantId, taskId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组建任务累加-授权用户数参数错误");
            return;
        }
        String key = this.getTaskAuthorizeUsersRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 组建任务-授权用户数错误:{}", e);
        } finally {
            this.updateAccumulationTaskAuthorizeUsers(tenantId, taskId, userId);
        }
    }

    @Override
    @Async
    public void accumulationTaskJoinGroupUsers(String tenantId, String groupId, String userId) {
        log.info(":>>> 开始组建任务-加入小组数,租户ID:{},小组ID:{},用户:{}", tenantId, groupId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(groupId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组建任务累加-加入小组数参数错误");
            return;
        }
        String key = this.getTaskJoinGroupRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 组建任务-加入小组数错误:{}", e);
        } finally {
            this.updateAccumulationTaskJoinGroup(tenantId, groupId, userId);
        }
    }

    @Override
    @Async
    public void accumulationTaskFirstTimeClockInUsers(String tenantId, String taskId, String userId, Date clockInTime) {
        log.info(":>>> 开始组建任务-首次打卡用户数,租户ID:{},小组ID:{},用户:{}", tenantId, taskId, userId);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(taskId) || StringUtils.isBlank(userId)) {
            log.error(":>>> 开始组建任务累加-首次打卡用户数参数错误");
            return;
        }
        String key = this.getTaskFirstClockInRedisKey(tenantId);
        try {
            this.updateCache(key);
        } catch (Exception e) {
            log.error(":>>> 设置租户:{} 组建任务-首次打卡用户数错误:{}", e);
        } finally {
            this.updateAccumulationTaskFirstClockIn(tenantId, taskId, userId);
        }
    }

    protected String getTaskParticipatingUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_TASK_PARTICIPATING_USERS_RPEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组建任务-参与数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getTaskActiveUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_TASK_ACTIVE_USERS_RPEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组建任务-激活用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getTaskAuthorizeUsersRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_TASK_AUTHORIZE_USERS_RPEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组建任务-授权用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getTaskJoinGroupRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_TASK_JOIN_GROUP_RPEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组建任务-加入小组用户数 Redis Key:{}", tenantId, key);
        return key;
    }

    protected String getTaskFirstClockInRedisKey(String tenantId) {
        String key = String.format(Constants.SYSTEM_TASK_FIRST_CLOCK_IN_RPEFIX, tenantId, DateTimeUtils.getDay0Str());
        log.info(":>>> 租户:{} 组建任务-首次用打卡户数 Redis Key:{}", tenantId, key);
        return key;
    }

        private void updateAccumulationTaskParticipatingUsers(String tenantId, String taskId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayFissionStatistics d = mongoTemplate.findOne(query, DayFissionStatistics.class);
        this.updateDayFissionStatistics(d, tenantId,
                startTime, "participateUsers", 1);
    }

        private void updateAccumulationTaskActiveUsers(String tenantId, String taskId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayFissionStatistics d = mongoTemplate.findOne(query, DayFissionStatistics.class);
        this.updateDayFissionStatistics(d, tenantId,
                startTime, "activationUsers", 1);
    }

        private void updateAccumulationTaskAuthorizeUsers(String tenantId, String taskId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayFissionStatistics d = mongoTemplate.findOne(query, DayFissionStatistics.class);
        this.updateDayFissionStatistics(d, tenantId,
                startTime, "authorizeUsers", 1);
    }

        private void updateAccumulationTaskJoinGroup(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayFissionStatistics d = mongoTemplate.findOne(query, DayFissionStatistics.class);
        this.updateDayFissionStatistics(d, tenantId,
                startTime, "joinGroupUsers", 1);
    }

        private void updateAccumulationTaskFirstClockIn(String tenantId, String groupId, String userId) {
        Query query = new Query();
        Date startTime = DateTimeUtils.getDay0Date();
        Criteria criteria = new Criteria().andOperator(Criteria.where("statisticsTime").gte(DateTimeUtils.dateToISODate(startTime)), Criteria.where("statisticsTime").lte(DateTimeUtils.dateToISODate(new Date(startTime.getTime() + 24 * 60 * 60 * 1000L))));
        query.addCriteria(criteria);
        DayFissionStatistics d = mongoTemplate.findOne(query, DayFissionStatistics.class);
        this.updateDayFissionStatistics(d, tenantId,
                startTime, "firstClockInUsers", 1);
    }

    /**
     * 更新Redis Key缓存
     *
     * @param key
     */
    private void updateCache(String key) {
        // TODO:RedisTemplate 序列化方式可能存在问题
        Long count = redisTemplate.opsForValue().increment(key, 1L);
        log.info(":>>> 计数器:{},结果为:{}", key, count);
        if (count.compareTo(0L) == 1) {
            redisTemplate.expire(key, keyExpire, TimeUnit.SECONDS);
            log.debug(":>>> 设置key:{} 过期时间24小时之后 ", key);
        }
    }

    /**
     * 更新裂变数据统计
     *
     * @param d
     * @param tenantId
     * @param startTime
     * @param field
     * @param incr
     */
        private void updateDayFissionStatistics(DayFissionStatistics d, String tenantId,
                                            Date startTime, String field, Integer incr) {
        if (Objects.isNull(d)) {
            d = new DayFissionStatistics();
            switch (field) {
                case "participateUsers":
                    d.setParticipateUsers(incr);
                    break;
                case "activationUsers":
                    d.setActivationUsers(incr);
                    break;
                case "authorizeUsers":
                    d.setAuthorizeUsers(incr);
                    break;
                case "joinGroupUsers":
                    d.setJoinGroupUsers(incr);
                    break;
                case "firstClockInUsers":
                    d.setFirstClockInUsers(incr);
                    break;
                default:
                    break;
            }
            d.setCreatedTime(new Date());
            d.setDeleted(Boolean.FALSE);
            d.setTenantId(tenantId);
            d.setStatisticsTime(startTime);
            mongoTemplate.insert(d);
            log.info(":>>> 租户:{} 组建任务-参与数:{} 统计不存在,新增", tenantId, startTime);
            return;
        }
        Query query1 = new Query();
        query1.addCriteria(Criteria.where("id").is(d.getId()));
        Update update = new Update();
        update.inc(field, incr);
        UpdateResult re = mongoTemplate.updateFirst(query1, update, DayFissionStatistics.class);
        log.info(":>>> 租户:{} 组建任务-参与数:{} 统计存在,修改结果:{}", tenantId, startTime, re.getModifiedCount() > 0 ? true : false);
    }
}
